//
//  main.cpp
//  opencvtest
//
//  Created by zzm on 16/8/30.
//  Copyright © 2016年 zzm. All rights reserved.
//
//
//  main.cpp
//  opencv1
//
//  Created by zzm on 2016/11/16.
//  Copyright © 2016年 zzm. All rights reserved.
//



#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstring>

#include "opencv2/opencv.hpp"
#include "../head/tcp_net_socket.hpp"
#include "../head/baidu_translate.hpp"


using namespace cv;
using namespace std;

void skinExtract(const Mat &frame, Mat &skinArea);
bool findfinger(Mat &frame, int &fingerX, int &fingerY);
int findeng(Mat &useRoi, vector<Rect> &rects, int fingerX, int fingerY);//返回离指尖最近的点的i
Mat cutUseROI(Mat &frame, int &fingerX, int &fingerY);

int fingerX = -1,fingerY = -1;//指尖坐标



int main(int argc, char* argv[])
{
    Mat frame;
    int time = 0;
    VideoCapture capture;
    
    capture.open(0);
    if (!capture.isOpened())
    {
        cout<<"No camera!\n"<<endl;
        return -1;
    }
    capture.set(CV_CAP_PROP_FRAME_WIDTH, 800);
    capture.set(CV_CAP_PROP_FRAME_HEIGHT, 600);
    string str;
    while(1)
    {
        
        
        capture >> frame;
        if (frame.empty())
            break;
        fingerX = -1,fingerY = -1;
        //寻找指尖坐标
        if(!findfinger(frame, fingerX, fingerY))continue;
//        cout << fingerX << " " << fingerY <<endl;
        
        //截取指尖上部分的区域，并只保留区域下部分的20%，再截取以指尖坐标左右长度为总x长度的10%
        if(fingerX < 0 || fingerY < 0)continue;
        Mat useRoi = cutUseROI(frame, fingerX ,fingerY);
        //寻找字母轮廓，分割，排序
        vector<Rect>rects;
        int KminNum;
        if((KminNum = findeng(useRoi, rects, fingerX, useRoi.cols)) == -1)continue;
        
        //使用训练好的KNN进行识别 or 直接使用tesseract
        char ch, flag=0;
        Mat fanyiRoi(useRoi(rects[KminNum]));
        string stranslate;
        time++;
        flag = 0;
        if(time == 10)
        {
            time = 0;
            cvtColor(fanyiRoi, fanyiRoi, CV_RGB2GRAY);
            int blockSize = 25;
            int constValue = 10;
            adaptiveThreshold(fanyiRoi, fanyiRoi, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, blockSize, constValue);
            imshow("look", fanyiRoi);
            imwrite("/root/t.png", fanyiRoi);
            system("/usr/bin/tesseract /root/t.png /root/out -l eng");
            ifstream fin("/root/out.txt");
            str = "";
            while(!fin.eof() )
            {
                ch = fin.get();
                if((ch >= 65 && ch <=90) || (ch >= 97 && ch <= 122))
                {
                    str += ch;
                    flag = 1;
                }
                
            }
            fin.close();
            time = 0;
            cout << endl;
            cout << "###############" << endl;
            cout << str << endl;
            
            
            //发送给baidu翻译进行翻译
            if(flag != 0){
            	stranslate = translate(str);
            	cout << "翻译后：" << stranslate << endl;
                //TTS输出
                string ekhocomm = "ekho '" + stranslate + "'";
                system(ekhocomm.c_str());
			}
    
        }
        imshow("fingeng", useRoi);
        if ( cvWaitKey(20) == 'q' )
            break;
    }

    return 0;
}

Mat cutUseROI(Mat &frame, int &fingerX, int &fingerY)
{

    int usePoint[2][2];
	fingerY -= 8;
    if((fingerX-(800*0.2)) < 0)usePoint[0][0] = 0;
    else usePoint[0][0] = fingerX-(800*0.2);

    if((fingerY-(600*0.2) < 0))usePoint[0][1] = 0;
    else usePoint[0][1] = fingerY-(600*0.2);
    //cout << "####1" << endl;
    usePoint[1][0] = (fingerX - usePoint[0][0]) + 160;
    if((usePoint[0][0] + usePoint[1][0]) >= 600){
        usePoint[1][0] = (600 - usePoint[0][0]) ;
       /// cout << "####" << usePoint[1][0] << endl;
    }
    if((fingerY-(600*0.2)) < 0)usePoint[1][1] = fingerY;
    else usePoint[1][1] = 600 * 0.2 ;
    if(usePoint[1][0] > 0 && usePoint[1][1] > 0){
        Mat useRoi(usePoint[1][0]+10, usePoint[1][1]+10, CV_8UC1, cv::Scalar(0));
    
        //cout << usePoint[0][0] << " " << usePoint[0][1] << " " << usePoint[1][0] << " " << usePoint[1][1] << " " << endl;
        
        useRoi = frame(Rect(usePoint[0][0], usePoint[0][1], usePoint[1][0], usePoint[1][1]));
        //cout << "####2" << endl;
        fingerY = usePoint[1][1];
        fingerX = fingerX - usePoint[0][0];
        
        return useRoi;
    }
    Mat useRoi(4, 4, CV_8UC3, Scalar(0,0,255));
    return useRoi;
}

int findeng(Mat &useRoi, vector<Rect> &rects, int fingerX, int fingerY)
{
    Mat temp;
    int temp_i = -1;
    vector<vector<Point> >contours;
    vector<Vec4i>hierarchy;
    cvtColor(useRoi, temp, CV_RGB2GRAY);
    threshold(temp, temp, 95, 255, CV_THRESH_BINARY_INV);
    Mat element = getStructuringElement(MORPH_RECT, Size(7, 8));
    dilate(temp, temp, element);
    findContours(temp, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point());
    
    Rect rc;
    vector<Rect>allRects;//所有轮廓的矩形
    Vector<long int> rectArces;//所有轮廓矩形的面积
    
    //膨胀
    
    
    //Vector<Rect>rects;//筛选出来的轮廓
    for(int i = 0; i < contours.size(); i++)
    {
        rc = boundingRect(contours[i]);
        rects.push_back(rc);
        rectangle(useRoi, rc, Scalar(255, 0, 0));

    }
    if(rects.size() <= 0)return -1;
    //筛选轮廓并找最近距离点
    int minTofinger = 65535;
    int KTofinger = 0;
    for(int i = 0; i < contours.size(); i++)
    {
        KTofinger = sqrt(pow(fabs(rects[i].x-fingerX)*4, 2)+pow(fabs(rects[i].y + rects[i].height -fingerY), 2));
//        cout << rects[i].x << " " << rects[i].y << endl;
//        cout << KTofinger << endl;
        if( KTofinger < minTofinger)
        {
            minTofinger = KTofinger;
            temp_i = i;
        }
    }
//    cout << "##########" << endl;
//    cout << fingerX << " " << fingerY << endl << "********" << endl;
    if(temp_i != -1)
        rectangle(useRoi, rects[temp_i], Scalar(0, 255, 0));
    return temp_i;
    
    
}

//寻找指尖
bool findfinger(Mat &frame, int &fingerX, int &fingerY)
{
    Mat showimg, skinArea;
    skinArea.create(frame.rows, frame.cols, CV_8UC1);
    skinExtract(frame, skinArea);
    if(skinArea.rows < 100 && skinArea.cols < 100) return false;
    frame.copyTo(showimg ,skinArea);
    
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    
    //寻找轮廓
    findContours(skinArea, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
    if(contours.size() < 10) {
	//imshow("show_img", frame);
        return false;
    }
    // 找到最大的轮廓
    int index(0);
    double area, maxArea(0);
    for (int i=0; i < contours.size(); i++)
    {
        area = contourArea(Mat(contours[i]));
        if (area > maxArea)
        {
            maxArea = area;
            index = i;
        }
    }
    
    //drawContours(frame, contours, index, Scalar(0, 0, 255), 2, 8, hierarchy );
    
    Moments moment = moments(skinArea, true);
    Point center(moment.m10/moment.m00, moment.m01/moment.m00);
    //        circle(frame, center, 8 ,Scalar(0, 0, 255), CV_FILLED);
    
    // 寻找指尖
    vector<Point> couPoint = contours[index];
    vector<Point> fingerTips;
    Point tmp;
    int max(0), count(0), notice(0);
    double Ktemp, Kmax = 0;//找离重点最远的点
    int tempnotice = 0;
    for (int i = 0; i < couPoint.size(); i++)
    {
        tmp = couPoint[i];
        int dist = (tmp.x - center.x) * (tmp.x - center.x) + (tmp.y - center.y) * (tmp.y - center.y);
        if (dist > max)
        {
            max = dist;
            notice = i;
        }
        
        // 计算最大值保持的点数，如果大于40（这个值需要设置，本来想根据max值来设置，
        // 但是不成功，不知道为何），那么就认为这个是指尖
        if (dist != max)
        {
            count++;
            if (count > 40)
            {
                count = 0;
                max = 0;
                bool flag = false;
                // 低于手心的点不算
                if (center.y < couPoint[notice].y )
                    continue;
                // 离得太近的不算
                for (int j = 0; j < fingerTips.size(); j++)
                {
                    if (abs(couPoint[notice].x - fingerTips[j].x) < 20)
                    {
                        flag = true;
                        break;
                    }
                }
                
                if (flag) continue;
                fingerTips.push_back(couPoint[notice]);
                Ktemp = sqrt(pow(fabs(couPoint[notice].x-center.x),2) + pow(fabs(couPoint[notice].y-center.y),2));
                if(Kmax < Ktemp)
                {
                    Kmax = Ktemp;
                    tempnotice = notice;
                }
            }
        }
    }
    
    circle(frame, couPoint[tempnotice], 6 ,Scalar(0, 255, 0), CV_FILLED);
    fingerX = couPoint[tempnotice].x;
    fingerY = couPoint[tempnotice].y;
    //line(frame, center, couPoint[tempnotice], Scalar(255, 0, 0), 2);
    //imshow("show_img", frame);
    return true;
}

//肤色提取，skinArea为二值化肤色图像
void skinExtract(const Mat &frame, Mat &skinArea)
{
    Mat YCbCr;
    vector<Mat> planes;
    
    //转换为YCrCb颜色空间
    cvtColor(frame, YCbCr, CV_RGB2YCrCb);
    //将多通道图像分离为多个单通道图像
    split(YCbCr, planes);
    
    //运用迭代器访问矩阵元素
    MatIterator_<uchar> it_Cb = planes[1].begin<uchar>(),
    it_Cb_end = planes[1].end<uchar>();
    MatIterator_<uchar> it_Cr = planes[2].begin<uchar>();
    MatIterator_<uchar> it_skin = skinArea.begin<uchar>();
    
    //人的皮肤颜色在YCbCr色度空间的分布范围:100<=Cb<=127, 138<=Cr<=170
    for( ; it_Cb != it_Cb_end; ++it_Cr, ++it_Cb, ++it_skin)
    {
        if (138 <= *it_Cr &&  *it_Cr <= 170 && 100 <= *it_Cb &&  *it_Cb <= 127)
            *it_skin = 255;
        else
            *it_skin = 0;
    }
    
    //膨胀和腐蚀，膨胀可以填补凹洞（将裂缝桥接），腐蚀可以消除细的凸起（“斑点”噪声）
    dilate(skinArea, skinArea, Mat(5, 5, CV_8UC1), Point(-1, -1));
    erode(skinArea, skinArea, Mat(5, 5, CV_8UC1), Point(-1, -1));
}
